/*
 *
 *  Copyright (C) THL A29 Limited, a Tencent company. All rights reserved.
 *  SPDX-License-Identifier: Apache-2.0
 *
 */
/* eslint-disable @typescript-eslint/naming-convention */
import {
  Button,
  Card,
  Form,
  H3,
  Icon,
  Input,
  InputNumber,
  Justify,
  Layout,
  List,
  Radio,
  RadioGroup,
  Select,
  Stepper,
  Text,
} from 'tea-component';
import React, { Ref, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import { useArrayRef, useGotoClick, useQueryParams } from '../../utils/hooks';
import { Control, Controller, useForm, useWatch } from 'react-hook-form';
import formUtils, { getStatus, Validator } from '../../utils/form-utils';
import { ChainCreateRequest } from '../../common/apis/chains/interface';
import {
  useChainCreate,
  useFetchChainConsensusList,
  useFetchChainDetail,
  useFetchChainDownload,
  useFetchChainModes,
} from '../../common/apis/chains/hooks';
import { UseFormTrigger } from 'react-hook-form/dist/types/form';
import { ControllerRenderProps } from 'react-hook-form/dist/types/controller';
import { useHistory } from 'react-router-dom';
import GlossaryGuide from '../../common/components/glossary-guide';
import { showChainAgreement } from './chain-agreement';
import { Checkbox, Switch } from 'tea-component/es';
import hljs from 'highlight.js';
import { Dictionary, keyBy, omit, stringifyQueryParams } from '../../utils/common';
import { ChainSubscribeContent } from './chain-subscribe';
import { renderChainAlgorithmFormItem } from '../certificates/organization-cert/modals/import-org-cert-modal';
import { ChainAlgorithm } from '../../common/apis/certificates/interface';
import NodeSelectorByRole from 'src/common/components/chainForm/node-selector-by-role';
import NodeSelectForPk from 'src/common/components/chainForm/node-selector-for-pk';
import StakeOp from 'src/common/components/chainForm/stake-op';
import ManagerOp from 'src/common/components/chainForm/manage-op';

const { Content } = Layout;
type ChainNewStep = 'deploy' | 'config' | 'subscribe';
type ChainNewQueryParam = {
  chainId: string;
  chainName: string;
  step: ChainNewStep;
};

const steps: { id: ChainNewStep; label: string }[] = [
  { id: 'config', label: '链参数配置' },
  { id: 'deploy', label: '部署长安链' },
  { id: 'subscribe', label: '订阅长安链' },
];

export default function ChainNew() {
  const handleBackClick = useGotoClick('/chains');
  const chainQueryParam = useQueryParams<ChainNewQueryParam>();
  const history = useHistory();
  const [current, setCurrent] = useState<ChainNewStep>(chainQueryParam.step || 'config');
  const onNext = useCallback((values: ChainNewQueryParam) => {
    history.push(`/chains/new?${stringifyQueryParams(values)}`);
  }, []);

  useEffect(() => {
    setCurrent(chainQueryParam.step || 'config');
  }, [chainQueryParam]);

  return (
    <>
      <Content.Header title="区块链管理/新建区块链" showBackButton onBackButtonClick={handleBackClick} />
      <Content.Body full>
        <Card>
          <Card.Body>
            <Justify className={'content-mb-4n'} left={<Stepper steps={steps} current={current} />} />
            {current === 'config' && <ChainConfig onNext={onNext} />}
            {current === 'deploy' && <ChainDeploy onNext={onNext} />}
            {current === 'subscribe' && <ChainSubscribe />}
          </Card.Body>
        </Card>
      </Content.Body>
    </>
  );
}

type ChainNode = ChainCreateRequest['Nodes'][0]['NodeList'][0];

function ChainConfig(props: { onNext: (values: ChainNewQueryParam) => void }) {
  const {
    control,
    trigger,
    formState: { isValidating, isSubmitted, isValid },
    getValues,
    setValue,
  } = useForm({
    mode: 'onBlur',
    defaultValues: {
      Tls: true,
      DockerVm: VM_OPTIONS.filter((item) => item.checked).map((item) => item.name),
      Monitor: '0',
      ChainmakerImprove: '1',
      Algorithm: ChainAlgorithm.NOT_CN.toString(),
      StakeMinCount: '',
      Stakes: [],
      BlockTxCapacity: 100,
      BlockInterval: 10,
      TxTimeout: 600,
      EnableHttp: false,
    } as any,
  });
  const deployFormRef = useRef<any>();
  const handleNextClick = useCallback(() => {
    const values = omit(getValues(), ['_Nodes', '_SyncNodes']) as ChainCreateRequest;
    const param = {
      ...values,
      DockerVm: (getValues('DockerVm') as string[]).find((item) => item === 'DOCKER_GO') ? 1 : 0,
      BlockTxCapacity: values.BlockTxCapacity ?? Validator.minBlockTxCapacity,
      BlockInterval: values.BlockInterval ?? Validator.minBlockInterval,
      TxTimeout: values.TxTimeout ?? Validator.minTxTimeout,
      ChainMode: values.ChainMode,
      Consensus: +values.Consensus,
      Algorithm: values.Algorithm ? +values.Algorithm : '',
      Monitor: +getValues('Monitor') as any,
      ChainmakerImprove: +getValues('ChainmakerImprove') as any,
      StakeMinCount: values.StakeMinCount ? +values.StakeMinCount : '',
      Tls: values.Tls ? 0 : 1,
      EnableHttp: values.EnableHttp ? 1 : 0,
      ...deployFormRef.current.getValues(),
    };
    if (param.ChainMode === 'permissionedWithCert') {
      delete param.Admins;
    } else {
      delete param.Tls;
    }
    if (param.Consensus !== 5) {
      delete param.StakeMinCount;
      delete param.Stakes;
    }
    onNext(param);
  }, []);
  const goBack = useGotoClick('/chains');

  const { list: consensusList, run: fetchConsensusList } = useFetchChainConsensusList();

  const consensusOptions = useMemo(
    () => consensusList.map((item) => ({ value: String(item.ConsensusType), text: item.ConsensusName })),
    [consensusList],
  );

  useEffect(() => {
    setValue('Consensus', String(consensusList[0]?.ConsensusType));
  }, [consensusList]);

  const { list: chainModes, run: fetchChainModes } = useFetchChainModes();
  // const { pkNodeList, run: fetchPkNodeList } = useFetchCertNodeList();

  const chainModesOptions = useMemo(() => {
    const result = chainModes.map((item) => ({
      value: item.ChainMode,
      text: `${item.ChainModeName}(${item.ChainMode})`,
    }));
    return result;
  }, [chainModes]);
  useEffect(() => {
    setValue('ChainMode', chainModesOptions[0]?.value);
  }, [chainModesOptions]);
  const selectedConsensus = useWatch({ name: 'Consensus', control });
  const selectedAlgorithm = useWatch({ name: 'Algorithm', control });
  const selectedChainMode = useWatch({ name: 'ChainMode', control });
  const selectedNodes: ChainCreateRequest['Nodes'] = useWatch({ name: '_Nodes', control });
  const selectedSyncNodes: ChainCreateRequest['Nodes'] = useWatch({ name: '_SyncNodes', control });
  const selectedAdmin: ChainCreateRequest['Admins'] = useWatch({ name: 'Admins', control });
  const [syncOrgListLen, setSyncOrgListLen] = useState<number>(0);

  const { run } = useChainCreate();
  const onNext = useCallback(
    async (values: ChainCreateRequest) => {
      run(values)
        .then(() => {
          props.onNext({
            chainId: values.ChainId,
            chainName: values.ChainName,
            step: 'deploy',
          });
        })
        .catch(() => null);
    },
    [props],
  );
  useEffect(() => {
    setValue('_Nodes', []);
    setValue('_SyncNodes', []);
    fetchConsensusList({ ChainMode: selectedChainMode });
  }, [selectedChainMode]);
  useEffect(() => {
    fetchChainModes();
  }, []);
  useEffect(() => {
    trigger('_Nodes');
  }, [selectedNodes, selectedConsensus]);
  useEffect(() => {
    if (selectedConsensus === '0') {
      setValue('_SyncNodes', []);
    }
  }, [selectedConsensus]);
  useEffect(() => {
    trigger('Admins');
  }, [selectedAdmin]);

  return (
    <>
      <Justify className={'tea-mt-5n'} left={<H3>基础信息</H3>} />
      <Form className={'tea-mt-5n tea-mb-5n'} layout="fixed" fixedLabelWidth={100}>
        <Controller
          control={control}
          rules={{
            validate: Validator.validateChainId,
          }}
          name="ChainId"
          render={({ field, fieldState }) => (
            <Form.Item
              label={<GlossaryGuide title={'区块链ID'} />}
              required
              status={formUtils.getStatus({
                fieldState,
                isValidating,
                isSubmitted,
              })}
              message={fieldState.error?.message}
            >
              <Input autoComplete="off" placeholder="请输入区块链ID" {...field} />
            </Form.Item>
          )}
        />
        <Controller
          control={control}
          rules={{
            validate: Validator.validateChainName,
          }}
          name="ChainName"
          render={({ field, fieldState }) => (
            <Form.Item
              status={formUtils.getStatus({
                fieldState,
                isValidating,
                isSubmitted,
              })}
              required
              label="区块链名称"
              message={fieldState.error?.message}
            >
              <Input placeholder="请输入区块链名称" {...field} autoComplete="off" />
            </Form.Item>
          )}
        />

        <Controller
          control={control}
          name="BlockTxCapacity"
          render={({ field, fieldState }) => (
            <Form.Item
              required
              status={formUtils.getStatus({
                fieldState,
                isValidating,
                isSubmitted,
              })}
              label={<GlossaryGuide title={'区块最大容量'} />}
              message={fieldState.error?.message}
            >
              <InputNumber
                min={Validator.minBlockTxCapacity}
                max={Validator.maxNumber}
                unit={'笔交易'}
                size={'l'}
                {...field}
              />
            </Form.Item>
          )}
        />
        <Controller
          control={control}
          name="BlockInterval"
          render={({ field, fieldState }) => (
            <Form.Item
              required
              status={formUtils.getStatus({
                fieldState,
                isValidating,
                isSubmitted,
              })}
              label={<GlossaryGuide title={'出块间隔'} />}
            >
              <InputNumber
                min={Validator.minBlockInterval}
                max={Validator.maxNumber}
                unit={'ms'}
                size={'l'}
                {...field}
              />
            </Form.Item>
          )}
        />
        <Controller
          control={control}
          name="TxTimeout"
          render={({ field, fieldState }) => (
            <Form.Item
              required
              status={formUtils.getStatus({
                fieldState,
                isValidating,
                isSubmitted,
              })}
              label={<GlossaryGuide title={'交易过期时长'} />}
            >
              <InputNumber min={Validator.minTxTimeout} max={Validator.maxNumber} unit={'s'} size={'l'} {...field} />
            </Form.Item>
          )}
        />
        <Controller
          control={control}
          name="ChainMode"
          rules={{
            required: true,
          }}
          render={({ field, fieldState }) => (
            <Form.Item
              required
              status={formUtils.getStatus({
                fieldState,
                isValidating,
                isSubmitted,
              })}
              label={<GlossaryGuide title={'链账户模式'} />}
              className={'tea-mt-1n'}
            >
              <Select options={chainModesOptions} {...field} />
            </Form.Item>
          )}
        />
        {selectedChainMode === 'permissionedWithCert' && (
          <Controller
            control={control}
            name="Tls"
            render={({ field }) => (
              <Form.Item label={<GlossaryGuide title={'是否开启TLS'} />}>
                <Switch {...field} />
              </Form.Item>
            )}
          />
        )}
        {renderChainAlgorithmFormItem(control, isSubmitted, isValidating, false, 'm')}
        <Controller
          control={control}
          name="EnableHttp"
          render={({ field }) => (
            <Form.Item label={<GlossaryGuide title="开启HTTP通讯" />}>
              <Switch {...field} />
            </Form.Item>
          )}
        />
        <Justify left={<H3>共识信息</H3>} className={'tea-mb-5n'} />
        <Controller
          control={control}
          name="Consensus"
          rules={{
            required: true,
          }}
          render={({ field, fieldState }) => (
            <Form.Item
              required
              status={formUtils.getStatus({
                fieldState,
                isValidating,
                isSubmitted,
              })}
              label={<GlossaryGuide title={'共识策略'} />}
              className={'tea-mt-1n'}
            >
              <Select options={consensusOptions} {...field} />
            </Form.Item>
          )}
        />
        <Controller
          control={control}
          name={'_Nodes'} // 临时属性，该字段并不提交后台
          defaultValue={[]}
          rules={{
            validate: (value: ChainCreateRequest['Nodes']) => {
              const nodes = value.reduce((res: ChainNode[], item) => {
                if (item.NodeList) {
                  return res.concat(item.NodeList);
                }
                return res;
              }, []);
              if (nodes.length === 0) {
                return '请选择共识节点';
              }
              if (selectedConsensus === '0' && nodes.length !== 1) {
                return '共识为SOLO则节点只能选择1个';
              }
              if (selectedConsensus === '4' && nodes.length < 3) {
                return '共识为RAFT则节点至少选择3个';
              }

              if (['1', '3', '5'].indexOf(selectedConsensus) > -1 && nodes.length < 4) {
                return '共识为TBFT、HOTSUFF、DPOS则至少需要选择4个节点';
              }
              return undefined;
            },
          }}
          render={({ field, fieldState }) => (
            <Form.Item
              required
              label="共识节点"
              className={'node-selector'}
              status={formUtils.getStatus({ fieldState, isSubmitted, isValidating })}
              message={fieldState.error?.message}
            >
              {selectedChainMode === 'permissionedWithCert' ? (
                <NodeSelectorByRole nodeRole={0} algorithm={selectedAlgorithm} {...field} />
              ) : (
                <NodeSelectForPk algorithm={selectedAlgorithm} nodeRole={0} chainMode={selectedChainMode} {...field} />
              )}
            </Form.Item>
          )}
        />
        {selectedChainMode === 'permissionedWithCert' && selectedConsensus !== '0' && (
          <Controller
            control={control}
            name={'_SyncNodes'} // 临时属性，该字段并不提交后台
            defaultValue={[]}
            rules={{
              validate: () => undefined,
            }}
            render={({ field, fieldState }) => (
              <Form.Item
                style={syncOrgListLen === 0 ? { display: 'none' } : {}}
                label="同步节点（选填）"
                className={'node-selector'}
                status={formUtils.getStatus({ fieldState, isSubmitted, isValidating })}
                message={fieldState.error?.message}
              >
                <NodeSelectorByRole
                  emitOrgList={setSyncOrgListLen}
                  nodeRole={1}
                  algorithm={selectedAlgorithm}
                  {...field}
                />
              </Form.Item>
            )}
          />
        )}
      </Form>

      <ChainDeployConfig
        ref={deployFormRef}
        selectedNodes={selectedNodes}
        selectedSyncNodes={selectedSyncNodes}
        control={control}
        trigger={trigger}
      />
      {selectedConsensus === '5' && selectedNodes?.length && (
        <StakeOp
          setValue={setValue}
          selectedNodes={selectedNodes}
          selectedChainMode={selectedChainMode}
          isSubmitted={isSubmitted}
          isValidating={isValidating}
          control={control}
          trigger={trigger}
        />
      )}
      {selectedChainMode !== 'permissionedWithCert' && (
        <ManagerOp
          isSubmitted={isSubmitted}
          isValidating={isValidating}
          selectedChainMode={selectedChainMode}
          control={control}
          algorithm={selectedAlgorithm}
        />
      )}
      <ChainVmConfig control={control} />
      <ChainLogConfig control={control} />
      <Justify
        className={'tea-mt-5n'}
        left={
          <>
            <Button onClick={goBack}>取消</Button>
            <Button type="primary" onClick={handleNextClick} disabled={!isValid}>
              下一步
            </Button>
          </>
        }
      />
    </>
  );
}

const shell_1 = hljs.highlight('$ docker pull chainmakerofficial/chainmaker-vm-engine:v2.3.2', {
  language: 'shell',
}).value;

const shell_2 = hljs.highlight(
  '$ cd /usr/local/src\n' +
    '\n' +
    '$ wget -c http://mirrors.kernel.org/fedora-epel/epel-release-latest-7.noarch.rpm\n' +
    '\n' +
    '$ rpm -ivh epel-release-latest-7.noarch.rpm\n' +
    '\n' +
    '$ yum repolist #查看是否启用\n' +
    '\n' +
    '$ yum install -y p7zip*',
  { language: 'shell' },
).value;

const shell_3 = hljs.highlight('$ sudo apt install p7zip-full', { language: 'shell' }).value;

const shell_4 = hljs.highlight('$ cd release\n$ ./start.sh', { language: 'shell' }).value;
const shell_5 = hljs.highlight(
  '$ #此处以示例节点为例，实际操作时请自行换成真实的名称\n' +
    '$ cd release\n' +
    '$ rm -rf TestCMorg1-示例测试节点1\n' +
    '$ ./start.sh',
  { language: 'shell' },
).value;
const shell_6 = hljs.highlight(
  '$ #此处以示例节点为例，实际操作时请自行换成真实的名称\n' +
    '$ cd release/TestCMorg1-示例测试节点1/bin\n' +
    '$ ./restart.sh',
  { language: 'shell' },
).value;
const shell_7 = hljs.highlight(
  '$ #查看节点进程是否存在\n' +
    '$ ps -ef|grep chainmaker | grep -v grep\n' +
    '$ #查看节点日志是否存在\n' +
    '$ cat ../release/*/log/system.log |grep "ERROR\\|put block\\|all necessary"\n' +
    '$ #若看到all necessary peers connected则表示节点已经准备就绪。',
  { language: 'shell' },
).value;

function ChainDeploy(props: { onNext: (values: ChainNewQueryParam) => void }) {
  const chainParams = useQueryParams<ChainNewQueryParam>();
  const { run, loading } = useFetchChainDownload();
  const { run: fetchChain, chain } = useFetchChainDetail();

  const handleDownloadClick = useCallback(() => {
    run(chainParams.chainId);
  }, []);

  const onNext = useCallback(() => {
    props.onNext({
      ...chainParams,
      step: 'subscribe',
    });
  }, [props]);

  useEffect(() => {
    fetchChain(chainParams.chainId);
  }, []);

  return (
    <>
      <Justify left={<H3>下载配置文件</H3>} className={'tea-mt-5n'} />
      <Form className={'tea-mt-5n'}>
        <Form.Item label={'链配置文件'}>
          <Text reset theme="text">
            {chainParams.chainName}.zip
            <Button type={'link'} onClick={handleDownloadClick} className={'tea-ml-5n'} disabled={loading}>
              <img src={loading ? 'static/icon/download_disabled.svg' : 'static/icon/download.svg'} />
              下载配置文件
            </Button>
            {loading && <Icon type="loading" />}
          </Text>
        </Form.Item>
      </Form>
      <Justify left={<H3>部署链教程</H3>} className={'tea-mt-5n'} />
      <List type="number" className={'tea-mt-5n'}>
        {chain?.DockerVm === 1 && (
          <List.Item>
            <div className={'tea-text-danger'}>
              如希望部署的链支持docker_go合约，则需要先拉取chainmaker-vm-docker-go镜像，以及安装7z工具，如果不需要docker-go合约则忽略此步骤。
            </div>
            <List type="bullet">
              <List.Item>
                拉取chainmaker-vm-docker-go镜像
                <pre
                  className={'code chain-code'}
                  dangerouslySetInnerHTML={{
                    __html: shell_1,
                  }}
                ></pre>
              </List.Item>
              <List.Item>
                CentOS系统安装7z工具
                <pre
                  className={'code chain-code'}
                  dangerouslySetInnerHTML={{
                    __html: shell_2,
                  }}
                ></pre>
              </List.Item>
              <List.Item>
                Ubuntu系统安装7z工具
                <pre
                  className={'code chain-code'}
                  dangerouslySetInnerHTML={{
                    __html: shell_3,
                  }}
                ></pre>
              </List.Item>
            </List>
          </List.Item>
        )}
        <List.Item>下载链配置文件压缩包</List.Item>
        <List.Item>
          单机部署的话，将压缩包移动到要部署的Linux系统机器上，解压压缩包，执行release
          目录下的start.sh脚本即可启动链，命令如下
          <p className={'tea-mt-2n'}>
            <pre
              className={'code chain-code'}
              dangerouslySetInnerHTML={{
                __html: shell_4,
              }}
            ></pre>
          </p>
        </List.Item>
        <List.Item>
          多机部署的话，首先需要保证机器间网络通畅，然后分别将压缩包移动到要部署的Linux系统机器上解压，部署方案如下
          <br />
          <br />
          方案一：
          <br />
          解压后分别进入部署机器的release目录，删除不需要部署在本机器的节点，然后执行start.sh脚本启动链，直至全部节点被成功启动。命令如下
          <p className={'tea-mt-2n'}>
            <pre
              className={'code chain-code'}
              dangerouslySetInnerHTML={{
                __html: shell_5,
              }}
            ></pre>
          </p>
          <br />
          方案二：
          <br />
          解压后分别进入部署机器的release/节点/bin目录，然后执行restart.sh脚本启动节点，直至全部节点被成功启动，命令如下
          <p className={'tea-mt-2n'}>
            <pre
              className={'code chain-code'}
              dangerouslySetInnerHTML={{
                __html: shell_6,
              }}
            ></pre>
          </p>
          <br />
          注意如果开启了报错日志采集服务，则只能选择方案一。
        </List.Item>
        <List.Item>
          可通过如下命令验证节点是否启动成功
          <p className={'tea-mt-2n'}>
            <pre
              className={'code chain-code'}
              dangerouslySetInnerHTML={{
                __html: shell_7,
              }}
            ></pre>
          </p>
        </List.Item>

        <List.Item>成功部署区块链后，点击下一步，用管理台订阅已部署的链。</List.Item>
      </List>
      <Justify
        className={'tea-mt-5n'}
        left={
          <Button type="primary" onClick={onNext}>
            下一步
          </Button>
        }
      />
    </>
  );
}

function ChainLogConfig({ control }: { control: Control }) {
  const showAgreementClick = useCallback(() => {
    showChainAgreement();
  }, []);

  const monitorWatch = useWatch({ name: 'Monitor', control });

  return (
    <>
      <Justify
        className={'tea-mt-5n'}
        left={
          <H3>
            <GlossaryGuide title={'报错日志采集服务（可选项）'} />
          </H3>
        }
      />
      <Form className={'content-mt-2n'} layout="fixed" fixedLabelWidth={100}>
        <Controller
          control={control}
          name="Monitor"
          rules={{}}
          render={({ field }) => (
            <Form.Item label={<GlossaryGuide title={'监控开关'} />} className={'tea-mt-1n'}>
              <ChainRadioGroup field={field as ControllerRenderProps} />
              <div className={'content-mt-1n'}>
                <Form.Text>
                  <Text>
                    请您先阅读
                    <Button type={'link'} onClick={showAgreementClick}>
                      《chainmaker管理台数据收集声明协议》
                    </Button>
                    再确定是否开启,开启监控开关即表示您同意本协议
                  </Text>
                </Form.Text>
              </div>
            </Form.Item>
          )}
        />
        {monitorWatch === '1' && (
          <>
            <Controller
              control={control}
              name="ChainmakerImprove"
              rules={{}}
              render={({ field }) => (
                <Form.Item label={<GlossaryGuide title={'是否参加长安链改进计划'} />} className={'tea-mt-1n'}>
                  <ChainRadioGroup field={field as ControllerRenderProps} />
                </Form.Item>
              )}
            />
          </>
        )}
      </Form>
    </>
  );
}

function ChainRadioGroup({ field }: { field: ControllerRenderProps }) {
  return (
    <RadioGroup {...field}>
      <Radio name="1">开启</Radio>
      <Radio name="0">关闭</Radio>
    </RadioGroup>
  );
}

const VM_OPTIONS: { name: string; title?: string; disabled: boolean; checked: boolean }[] = [
  {
    name: 'DOCKER_GO',
    disabled: false,
    checked: false,
  },
  {
    name: 'WASMER',
    disabled: true,
    checked: true,
  },
  {
    name: 'EVM',
    disabled: true,
    checked: true,
  },
  {
    name: 'WXVM',
    disabled: true,
    checked: true,
  },
  {
    name: 'GASM',
    disabled: true,
    checked: true,
  },
];

const DEPLOY_OPTIONS: typeof VM_OPTIONS = [
  {
    name: '0',
    title: '单机部署',
    disabled: false,
    checked: true,
  },
  {
    name: '1',
    title: '多机部署',
    disabled: false,
    checked: false,
  },
];

function ChainDeployConfigContainer(
  props: {
    selectedNodes: ChainCreateRequest['Nodes'];
    selectedSyncNodes: ChainCreateRequest['Nodes'];
    control: Control;
    trigger: UseFormTrigger<any>;
  },
  ref: Ref<any>,
) {
  const { control } = useForm({
    mode: 'onBlur',
    defaultValues: {
      Single: '0',
    } as any,
  });
  const deployMode = useWatch({ name: 'Single', control });
  const [nodeMap, setNodeMap] = useState<Dictionary<ChainNode>>({});
  const [firstIp, setFirstIp] = useState<string>('');
  const [configRowRefs] = useArrayRef();
  useEffect(() => {
    // 组织节点树转化为节点列表
    const nodes = (props.selectedNodes || []).reduce((res: ChainNode[], item) => res.concat(...item.NodeList), []);
    const allNodes = (props.selectedSyncNodes || []).reduce(
      (res: ChainNode[], item) => res.concat(...item.NodeList),
      nodes,
    );
    const newNodeMap = keyBy(allNodes, 'NodeName');

    setFirstIp(configRowRefs[0]?.getValues().NodeIp);
    setNodeMap(newNodeMap);
  }, [props.selectedNodes, props.selectedSyncNodes]);
  const updateIp = useCallback(() => {
    if (deployMode === DEPLOY_OPTIONS[0].name && !configRowRefs[0]?.getValues().NodeIp && firstIp) {
      configRowRefs[0]?.setIp(firstIp);
    }
  }, [deployMode, configRowRefs, firstIp]);
  useEffect(() => {
    updateIp();
  }, [nodeMap]);

  useImperativeHandle(
    ref,
    () => ({
      getValues: () => {
        const map = configRowRefs.reduce((res, ref, index) => {
          const node = ref.getValues();
          if (deployMode === DEPLOY_OPTIONS[0].name && index > 0) {
            node.NodeIp = configRowRefs[0].getValues().NodeIp;
          }
          res[node.NodeName] = node;
          return res;
        }, {});
        const result: {
          Single: number;
          Nodes: any[];
          CommonNodes?: any[];
        } = {
          Nodes: props.selectedNodes.map((item) => {
            const res: {
              NodeList: any[];
              OrgId?: string;
            } = {
              NodeList: item.NodeList.map((item) => map[item.NodeName]),
            };
            if (item.OrgId) {
              res.OrgId = item.OrgId;
            }
            return res;
          }),
          Single: +deployMode,
        };
        if (props.selectedSyncNodes.length) {
          result.CommonNodes = props.selectedSyncNodes.map((item) => ({
            ...item,
            NodeList: item.NodeList.map((item) => map[item.NodeName]),
          }));
        }
        return result;
      },
    }),
    [nodeMap, configRowRefs, deployMode],
  );

  return (
    <Controller
      control={props.control}
      name={'Nodes'}
      rules={{
        validate: () => {
          if (configRowRefs.every((item) => item.isValid)) {
            return;
          }
          return '请填写相关配置';
        },
      }}
      render={() => (
        <div>
          <Justify
            className={'tea-mt-5n'}
            left={
              <H3>
                <GlossaryGuide title={'节点部署配置'} />
              </H3>
            }
          />
          <Form className={'content-mt-2n content-mb-2n'} layout="fixed" fixedLabelWidth={100}>
            <Controller
              control={control}
              name="Single"
              render={({ field }) => (
                <Form.Item label={<GlossaryGuide title={'部署方式'} />} className={'tea-mt-1n'}>
                  <RadioGroup {...field}>
                    {DEPLOY_OPTIONS.map((item) => (
                      <Radio key={item.name} name={item.name}>
                        {item.title}
                      </Radio>
                    ))}
                  </RadioGroup>
                </Form.Item>
              )}
            />
          </Form>
          {Object.values(nodeMap).map((item, index) => (
            <ChainNodeConfigRow
              index={index}
              node={item}
              deployMode={deployMode}
              key={item.NodeName}
              onValidChange={() => props.trigger('Nodes')}
              ref={(ref) => (configRowRefs[index] = ref)}
            />
          ))}
        </div>
      )}
    />
  );
}

function ChainNodeConfigRowContainer(
  {
    index,
    deployMode,
    node: item,
    onValidChange,
  }: {
    index: number;
    node: ChainNode;
    deployMode: string;
    onValidChange: (isValid: boolean) => void;
  },
  ref: any,
) {
  const {
    control,
    getValues,
    setValue,
    formState: { isValidating, isSubmitted, isValid },
  } = useForm({
    mode: 'onBlur',
  });
  /**
   * 单机模式下，只需要在第一个节点输入IP即可
   */
  let ipInputDisabled = false;
  if (deployMode === DEPLOY_OPTIONS[0].name && index > 0) {
    ipInputDisabled = true;
    setValue('NodeIp', '');
  }

  useImperativeHandle(ref, () => {
    const values = getValues();
    return {
      isValid,
      getValues: () => ({
        NodeName: item.NodeName,
        NodeRpcPort: +values.NodeRpcPort,
        NodeP2pPort: +values.NodeP2pPort,
        NodeIp: values.NodeIp,
      }),
      setIp: (ip: string) => {
        setValue('NodeIp', ip);
      },
    };
  });

  useEffect(() => {
    onValidChange(isValid);
  }, [isValid]);

  return (
    <Form layout={'inline'} key={item.NodeName}>
      <Form.Item key={item.NodeName} label={<div className={'chain-deploy-node-name'}>{item.NodeName}</div>} />

      <Controller
        name={'NodeIp'}
        control={control}
        rules={{
          validate: (input) => {
            if (ipInputDisabled) {
              return undefined;
            }
            return Validator.validateIP(input);
          },
        }}
        render={({ field, fieldState }) => (
          <Form.Item
            key={item.NodeName}
            status={getStatus({
              fieldState,
              isSubmitted,
              isValidating,
            })}
            message={fieldState.error?.message}
          >
            <Input placeholder={ipInputDisabled ? '同上' : 'IP地址'} disabled={ipInputDisabled} {...field} />
          </Form.Item>
        )}
      />
      <Controller
        control={control}
        rules={{
          validate: (input: string) => {
            if (input && getValues('NodeP2pPort') === input) {
              return 'RPC端口与P2P端口不可以相同';
            }
            return Validator.validatePort(input);
          },
        }}
        render={({ field, fieldState }) => (
          <Form.Item
            status={getStatus({ fieldState, isSubmitted, isValidating })}
            className={'tea-ml-2n'}
            message={fieldState.error?.message}
          >
            <Input placeholder={'RPC端口'} {...field} />
          </Form.Item>
        )}
        name={'NodeRpcPort'}
      />
      <Controller
        control={control}
        rules={{
          validate: (input: string) => {
            if (input && getValues('NodeRpcPort') === input) {
              return 'RPC端口与P2P端口不可以相同';
            }
            return Validator.validatePort(input);
          },
        }}
        render={({ field, fieldState }) => (
          <Form.Item
            status={getStatus({ fieldState, isSubmitted, isValidating })}
            className={'tea-ml-2n'}
            message={fieldState.error?.message}
          >
            <Input placeholder={'P2P端口'} {...field} />
          </Form.Item>
        )}
        name={'NodeP2pPort'}
      />
    </Form>
  );
}

const ChainNodeConfigRow = React.forwardRef(ChainNodeConfigRowContainer);

const ChainDeployConfig = React.forwardRef(ChainDeployConfigContainer);
// const ManagerOpConfig = React.forwardRef(ManagerOp);

function ChainVmConfigContainer({ control }: { control: Control }) {
  return (
    <div>
      <Justify
        className={'tea-mt-5n'}
        left={
          <H3>
            <GlossaryGuide title={'合约虚拟机配置'} />
          </H3>
        }
      />
      <Form className={'content-mt-2n'} layout="fixed" fixedLabelWidth={100}>
        <Controller
          control={control}
          name="DockerVm"
          rules={{}}
          render={({ field }) => (
            <Form.Item label={'支持的虚拟机类型'} className={'tea-mt-1n'}>
              <Checkbox.Group {...field}>
                {VM_OPTIONS.map((item) => (
                  <Checkbox key={item.name} name={item.name} disabled={item.disabled}>
                    {item.name}
                  </Checkbox>
                ))}
              </Checkbox.Group>
            </Form.Item>
          )}
        />
        <div className={'tea-form__item text-size-base'}>
          <div className={'tea-form__label'} />
          <div
            className={'text-pre-line'}
            dangerouslySetInnerHTML={{
              __html: `默认支持WASMER、EVM、WXVM、GASM，支持Rust、Solidity，C++语言的合约。\n如果您想部署Go语言的合约的话，需要勾选支持Docker_go，勾选后在部署链时除链的主程序外，还会再额外部署一个DockerVM。`,
            }}
          />
        </div>
      </Form>
    </div>
  );
}

const ChainVmConfig = ChainVmConfigContainer;

function ChainSubscribe() {
  const queryParams = useQueryParams<ChainNewQueryParam>();
  const history = useHistory();
  const goBack = useCallback(() => {
    history.push(
      `/chains/new?${stringifyQueryParams({
        ...queryParams,
        step: 'deploy',
      })}`,
    );
  }, [queryParams]);
  return (
    <>
      <ChainSubscribeContent cancelBtnText={'上一步'} submitBtnText={'确认订阅'} cancelBtnClick={goBack} />
    </>
  );
}
